package cn.wuyi.common.util;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.fasterxml.jackson.databind.util.JSONPObject;

/**
 * @类功能说明:  封装jackson mapper相关便捷方法的工具类
 * @版本                ：V1.0
 */
public class JacksonUtil {

	private static final Logger log = LoggerFactory.getLogger(JacksonUtil.class);

	private ObjectMapper mapper;

	public JacksonUtil(Include include) {
		mapper = JacksonMapper.getInstance(include);
	}

	public JacksonUtil(ObjectMapper mapper) {
		super();
		this.mapper = mapper;
	}

	/**
	 * @方法功能说明：创建只输出非Null且非Empty(如List.isEmpty)的属性到Json字符串的Mapper,建议在外部接口中使用。
	 * 1.将该标记放在属性上，如果该属性为NULL则不参与序列化;如果放在类上边,那对这个类的全部属性起作用.
	 * 2.属性为 空（“”） 或者为 NULL 都不序列化
	 * @作者        : Administrator
	 * @创建时间    ：2016年10月9日 下午12:43:30 
	 * @return 只输出非Null且非Empty的属性到Json字符串的Mapper
	 */
	public static JacksonUtil nonEmptyMapper() {
		return new JacksonUtil(Include.NON_EMPTY);
	}

	/**
	 * @方法功能说明：创建只输出初始值被改变的属性到Json字符串的Mapper, 最节约的存储方式，建议在内部接口中使用。
	 * 属性为默认值不序列化
	 * @作者        : Administrator
	 * @创建时间    ：2016年10月9日 下午12:45:18 
	 * @return 只输出初始值被改变的属性到Json字符串的Mapper
	 */
	public static JacksonUtil nonDefaultMapper() {
		return new JacksonUtil(Include.NON_DEFAULT);
	}

	/**
	 * @方法功能说明：创建只输出非Null的属性到Json字符串的Mapper
	 * @作者        : Administrator
	 * @创建时间    ：2016年10月9日 下午12:56:22 
	 * @return 创建只输出非Null的属性到Json字符串的Mapper
	 */
	public static JacksonUtil nonNullMapper() {
		return new JacksonUtil(Include.NON_NULL);
	}

	/**
	 * 创建默认的Mapper
	 */
	public static JacksonUtil defaultMapper() {
		return new JacksonUtil(Include.ALWAYS);
	}
	
	/**
	 * @方法功能说明：创建默认的Mapper
	 * @作者        : Administrator
	 * @创建时间    ：2016年10月9日 下午12:56:09 
	 * @return
	 */
	public String toJson(Object object) {
		try {
			return mapper.writeValueAsString(object);
		} catch (IOException e) {
			log.warn("write to json string error:" + object, e);
			return null;
		}
	}
	
	/**
	 * @方法功能说明：Json转List<T>
	 * @作者        : 
	 * @创建时间    ：2018年1月16日 下午5:05:57 
	 * @param json
	 * @param t List集合存放的类型
	 * @return
	 */
	public <T> List<T> fromJsonToList(String json,Class<T> t){
		JavaType javaType = createCollectionType(List.class, t);
		List<T> list = fromJson(json, javaType);
		return list;
	}
	
	/**
	 * @方法功能说明：反序列化POJO或简单Collection
	 * 如List<String>,如果JSON字符串为Null或"null"字符串, 返回Null. 如果JSON字符串为"[]", 返回空集合.
	 * 如需反序列化复杂Collection如List<MyBean>, 请使用fromJson(String,JavaType)
	 * @作者        : Administrator
	 * @创建时间    ：2016年10月9日 下午12:54:30 
	 * @param jsonString
	 * @param clazz
	 * @return
	 * @see #fromJson(String, com.fasterxml.jackson.databind.JavaType)
	 */
	public <T> T fromJson(String jsonString, Class<T> clazz) {
		if (StringUtils.isEmpty(jsonString)) {
			return null;
		}
		try {
			return mapper.readValue(jsonString, clazz);
		} catch (IOException e) {
			log.warn("parse json string error:" + jsonString, e);
			return null;
		}
	}

	/**
	 * @方法功能说明：反序列化复杂Collection如List<Bean>, 先使用函数createCollectionType构造类型,然后调用本函数.
	 * @作者        : Administrator
	 * @创建时间    ：2016年10月9日 下午12:53:51 
	 * @param jsonString
	 * @param javaType
	 * @return
	 * @see #createCollectionType(Class, Class...)
	 */
	public <T> T fromJson(String jsonString, JavaType javaType) {
		if (StringUtils.isEmpty(jsonString)) {
			return null;
		}
		try {
			return mapper.readValue(jsonString, javaType);
		} catch (IOException e) {
			log.warn("parse json string error:" + jsonString, e);
			return null;
		}
	}

	/**
	 * @方法功能说明：构造泛型的Collection Type 
	 * 如: ArrayList<MyBean>,则调用constructCollectionType(ArrayList.class,MyBean.class) 
	 * HashMap<String,MyBean>, 则调用(HashMap.class,String.class, MyBean.class)
	 * @作者        : Administrator
	 * @创建时间    ：2016年10月9日 下午12:52:22 
	 * @param collectionClass
	 * @param elementClasses
	 * @return
	 */
	public JavaType createCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
		return mapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
	}

	public JavaType createJavaType(Class<?> clazz, JavaType javaType) {
		return mapper.getTypeFactory().constructParametricType(clazz, javaType);
	}

	/**
	 * @方法功能说明：当JSON里只有Bean的部分属性时，更新一个已存在的Bean，只覆盖该部分的值。
	 * @作者        : Administrator
	 * @创建时间    ：2016年10月9日 下午12:53:24 
	 * @param jsonString
	 * @param object
	 * @return
	 */
	public <T> T update(String jsonString, T object) {
		try {
			return mapper.readerForUpdating(object).readValue(jsonString);
		} catch (JsonProcessingException e) {
			log.warn("update json string:" + jsonString + " to object:" + object + " error.", e);
		} catch (IOException e) {
			log.warn("update json string:" + jsonString + " to object:" + object + " error.", e);
		}
		return null;
	}

	/**
	 * @方法功能说明：输出JSONP格式Text，格式为functionName(json)。
	 * @作者        : Administrator
	 * @创建时间    ：2016年10月9日 下午12:47:14 
	 * @param functionName
	 * @param object
	 * @return
	 */
	public String toJsonP(String functionName, Object object) {
		return toJson(new JSONPObject(functionName, object));
	}

	/**
	 * @方法功能说明：设定是否使用Enum的toString函数来读写Enum, 为False时，使用Enum的name()函数来读写Enum, 默认为False.
	 * 注意本函数一定要在Mapper创建后, 所有的读写动作之前调用.
	 * @作者        : Administrator
	 * @创建时间    ：2016年10月9日 下午12:47:28
	 */
	public void enableEnumUseToString() {
		mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
		mapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
	}
	
	/**
	 * @方法功能说明：设定格式化Date类型数据格式 在所有读写动作之前调用
	 * @作者        : Administrator
	 * @创建时间    ：2016年10月9日 下午12:47:54 
	 * @param dateFormat
	 */
	public void setDateFormat(String dateFormat) {
		mapper.setDateFormat(new SimpleDateFormat(dateFormat));
	}

	/**
	 * 取出Mapper做进一步的设置或使用其他序列化API.
	 */
	public ObjectMapper getMapper() {
		return mapper;
	}

	/**
	 * @方法功能说明：Json转List<Map>
	 * @作者        : Administrator
	 * @创建时间    ：2016年10月9日 下午1:07:12 
	 * @param json
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List<Map<String, Object>> fromJsonToList(String json) {
		try {
			return mapper.readValue(json, List.class);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("解析json错误");
		}
	}
	
	/**
	 * @方法功能说明：json转为Map<String,Object>
	 * @作者        : 
	 * @创建时间    ：2017年8月1日 下午1:27:57 
	 * @param json
	 * @return
	 */
	public Map<String,Object> fromJsonToMap(String json){
		return fromJsonToMap(json,String.class,Object.class);
	}
	
	/**
	 * @方法功能说明：json转为Map<T,E>
	 * @作者        : 
	 * @创建时间    ：2017年8月1日 下午1:28:26 
	 * @param json
	 * @param t
	 * @param e
	 * @return
	 */
	public <T, E> Map<T,E> fromJsonToMap(String json,Class<T> t,Class<E> e){
		JavaType javaType = createCollectionType(Map.class, t,e);
		return fromJson(json, javaType);
	}
	
	/**
	 * @方法功能说明：过滤属性
	 * 使用方法:先给ObjectMapper添加一个filter，然后还要在需要过滤的类上加@JsonFilter("filterName")注解。
	 * 比如说要过滤User 上的name属性，先
	 * Jacksons.json().filter("myFilter", "name").readAsString(user)，具体看Jacksons代码。并在User类上加@JsonFilter("myFilter")。
	 * 有点不爽的是如果用另外一个没有添加该filter的ObjectMapper解析的话会报错。	 	
	 * 如果这个User类已经添加了@JsonFilter("myFilter")注解，但在另外一个地方又要解析它并不想过滤name 属性，那只能是
	 * 	Jacksons.json().filter("myFilter", "")，然后在读出来。
	 * @作者        : Administrator
	 * @创建时间    ：2016年10月9日 下午12:51:15 
	 * @param filterName
	 * @param properties
	 * @return
	 */
	public JacksonUtil filter(String filterName, String... properties) {
		//FilterProvider filterProvider = new SimpleFilterProvider().addFilter(filterName,SimpleBeanPropertyFilter.serializeAllExcept(properties));
		SerializationConfig serializationConfig = mapper.getSerializationConfig();
		SimpleFilterProvider filterProvider = (SimpleFilterProvider) serializationConfig.getFilterProvider();
		if(filterProvider == null){
			filterProvider = new SimpleFilterProvider();
		}
		filterProvider.addFilter(filterName,SimpleBeanPropertyFilter.serializeAllExcept(properties));
		mapper.setFilterProvider(filterProvider);
		return this;
	}

}